using System;
using System.Collections.Generic;
using System.Collections.Specialized;
#if !SANDBOX
using System.Reflection;
#endif
using System.Text;
using System.Xml.XPath;
using Microsoft.SharePoint;
using PS.Practices.Provisioner;
using PS.Practices.SiteBuilder.Features;
using PS.Practices.SiteBuilder.Features.Schema;

namespace PS.Practices.Provisioner.Util
{
    public static class ListUtil
    {
        public static SPListItem FindListItemByFieldValue(SPList list, string field, string value)
        {
            SPListItem item = null;
            try
            {
                
                SPQuery query = new SPQuery();

                query.Query = string.Concat(
                    "<Where><Eq>",
                        "<FieldRef Name='" + field + "' />",
                        "<Value Type='Text'>" + value + "</Value>",
                    "</Eq></Where>");

                SPListItemCollection col = list.GetItems(query);

                if (col.Count > 0)
                    item = col[0];
            }
            catch(Exception ex)
            {
                Logger.Error("PS.Practices.Util.ListUtil.FindListItemByFieldValue: " + ex.ToString()); 
            }

            return item;
        }

        //TODO Add type/required options
        public static void EnsureListField(SPList list, string name)
        {
            try
            {
                if (!list.Fields.ContainsField(name))
                {
                    var field = list.Fields.Add(name, SPFieldType.Text, false);
                    list.Update();

                    SPView defView = list.DefaultView;
                    defView.ViewFields.Add(list.Fields.GetField(field));
                    defView.Update();
                }
            }
            catch(Exception ex)
            {
                Logger.Error("PS.Practices.Util.ListUtil.EnsureListField: " + ex.ToString()); 
            }
        }

        public static object TryGetListItemProperty(SPListItem item, string property)
        {
            object value = "";

            try
            {
                SPField field = item.ParentList.Fields.GetField(property);

                value = (object)field.GetFieldValue(item[field.InternalName].ToString());
            }
            catch (Exception ex)
            {
                Logger.Error("PS.Practices.Util.ListUtil.TryGetListItemProperty: " + ex.ToString());
            }

            return value;
        }

        public static SPListItemCollection Query(this SPList list, string camlQuery)
        {
            return list.GetItems(new SPQuery { Query = camlQuery });
        }

        public static void PerformOperation(string webUrl, string listName, Action<SPList> op)
        {
            using (SPSite site = new SPSite(webUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPList list;
                    try
                    {
                        list = web.Lists[listName];
                    }
                    catch (ArgumentException)
                    {
                        throw new ArgumentException("Unable to open list : " + listName + " in web " +
                                                               web.Url);
                    }
                    op(list); 
                }
            }
        }

        public static void ApplyScript(SPList list, XPathNavigator script)
        {
            Interpreter parser = new Interpreter();
            parser.Process(list, script);
        }

        public static void ReorderCTypes(SPList list)
        {
            SPFolder root = list.RootFolder;
            if (root == null )
                return;

            IList<SPContentType> ctypes;
            try
            {
                ctypes = root.ContentTypeOrder;
            }
            catch (SPException)
            {
                ctypes = new List<SPContentType>();
                foreach (SPContentType type in list.ContentTypes)
                {
                    if (type.Name != "Folder")
                        ctypes.Add(type);
                }
            }
            if (ctypes == null) return;
            try
            {
                List<SPContentType> ret = new List<SPContentType>(ctypes);
                ret.Sort(CompareCTypesByName);
                if (ret.Count > 0)
                {
                    try
                    {
                        root.UniqueContentTypeOrder = ret;
                    }
                    catch (Exception)
                    {
                        root.UniqueContentTypeOrder = new List<SPContentType>();
                        root.Update();
                    }
                    
                    root.Update();
                }
            }
            catch (SPException){  }
        }

        public static int CompareCTypesByName(SPContentType x, SPContentType y)
        {
            return x.Name.CompareTo(y.Name);
        }

        public static int CompareFieldsByBame(SPField x, SPField y)
        {
            return x.Title.CompareTo(y.Title);
        }

        public static bool AddCTypeByName(SPList list, string name)
        {
            SPContentType type;
            type = ResolveContentTypeByName(name, list);

            if (type != null)
                try
                {
                    list.ContentTypes.Add(type);
                    return true;
                }
                catch (SPException)
                {
                }
            return false;
        }


        public static bool AddCType(SPList list, string id)
        {
            SPContentType type;
            type = ResolveContentType(id, list);

            if (type != null)
                try
                {
                    list.ContentTypes.Add(type);
                    return true;
                } catch(SPException)
                {
                }
            return false;
        }

        public static bool RemoveCType(SPList list, string id)
        {
            try
            {
                SPContentTypeId ctypeId = new SPContentTypeId(id);
                SPContentTypeCollection types = list.ContentTypes;

                ctypeId = types.BestMatch(ctypeId);
                
                types.Delete(ctypeId);
                return true;
            } catch(ArgumentOutOfRangeException)
            {
                
            }
            return false;
        }

        public static SPContentType ResolveContentTypeByName(string name, SPList list)
        {
            SPContentType type = null;
            SPWeb curWeb = list.ParentWeb;

            SPWeb web = curWeb.Site.RootWeb;
            
            try
            {
                type = web.ContentTypes[name];
            }
            catch (SPException)
            {
            }

            return type;
        }

        public static SPContentType ResolveContentType(string strid, SPList list)
        {
            SPContentType type;
            SPContentTypeId id = new  SPContentTypeId(strid);
            SPWeb curWeb = list.ParentWeb;
            do
            {
                type = curWeb.ContentTypes[id];
                curWeb = curWeb.ParentWeb;
                
            } while (curWeb!=null && type == null);
            return type;
        }

        public static void InjectFieldsFromCTypes(SPView view, SPContentTypeCollection ctypes, int pos, StringCollection less)
        {
            try
            {
                Dictionary<string, SPField> fields = new Dictionary<string, SPField>(128);
                SPViewFieldCollection viewFields = view.ViewFields;
                if (less == null)
                    less = new StringCollection();

                foreach (SPContentType type in ctypes)
                {
                    foreach (SPField field in type.Fields)
                    {
                        string title = field.StaticName;
                        if (!fields.ContainsKey(title) && !viewFields.Exists(title) && !less.Contains(title))
                            fields.Add(title, field);
                    }
                }

                List<SPField> flds = new List<SPField>(fields.Values);
                flds.Sort(CompareFieldsByBame);

                StringCollection origFields = viewFields.ToStringCollection();
                
                viewFields.DeleteAll();

                for (int a = 0; a < pos; a++)
                {
                    try
                    {
                        viewFields.Add(origFields[a]);
                    }
                    catch (ArgumentException)
                    {
                    }
                }

                foreach (SPField field in flds)
                {
                    try
                    {
                        viewFields.Add(field);
                    }
                    catch (ArgumentException)
                    {
                    }
                }


                for (int a = pos; a < origFields.Count; a++)
                {
                    try
                    {
                        viewFields.Add(origFields[a]);
                    }
                    catch (ArgumentException)
                    {
                    }
                }

                view.Update();
            } catch (Exception e)
            {
                Logger.Error(e);
            }
        }

        public static void LinkFieldToListCTypes(SPField field, SPList list)
        {
            StringBuilder sb = new StringBuilder(128);
            foreach (SPContentType cType in list.ContentTypes)
            {
                if (!cType.Sealed  && !cType.Hidden && !cType.Fields.ContainsField(field.Title))
                {
                    try
                    {
                        cType.FieldLinks.Add(new SPFieldLink(field));
                        cType.Update();
                        sb.Append(cType.Name + ";");
                    } catch (SPException){}
                }
            }
            Logger.Info("Added to content types: " + sb);
        }

        public static void SyncReqHidFieldStatusInLists(SPWeb web)
        {
            SPListCollection lists = web.Lists;
            for (int a = 0; a < lists.Count; a++)
            {
                SPList list = lists[a];
                if (list is SPDocumentLibrary && list.ContentTypesEnabled)
                {
                    SPContentType ctype;
                    ctype = list.ContentTypes[0];
                    SPContentType parentCType = ctype.Parent;
                    foreach (SPFieldLink fld in ctype.FieldLinks)
                    {
                        try
                        {
                            SPField field = parentCType.Fields[fld.Id];
                            SPFieldLink fLink = parentCType.FieldLinks[fld.Id];
                            SPField lstFld = list.Fields[fld.Id];
                            if (field != null && fLink != null && lstFld != null && !field.Sealed &&
                                field.Group != "_Hidden")
                            {
                                lstFld.Required = fLink.Required;
#if !SANDBOX
                                if (!lstFld.CanToggleHidden)
                                {
                                    Type type = lstFld.GetType();
                                    MethodInfo mi = type.GetMethod("SetFieldBoolValue", BindingFlags.NonPublic | BindingFlags.Instance);
                                    mi.Invoke(lstFld, new object[] {"CanToggleHidden", true});
                                }
#endif
                                lstFld.Hidden = fLink.Hidden;
                                lstFld.Update(false);
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }
                    //OLD METHOD
                    //SPListCollection lists = web.Lists;
                    //for(int a = 0; a<lists.Count; a++)
                    //{
                    //    SPList list = lists[a];
                    //    if (list is SPDocumentLibrary && list.ContentTypesEnabled)
                    //    {
                    //        SPContentType ctype = null;
                    //        int c = 0;
                    //        foreach (SPContentType t in list.ContentTypes)
                    //        {
                    //            if (t.Name == "Folder")
                    //                continue;
                    //            c++;
                    //            ctype = t;
                    //        }

                    //        if (c == 1 && ctype != null)
                    //        {
                    //            Logger.Info(
                    //                "Syncronizing local list fields from the single provisioned content type");

                    //            foreach (SPField fld in ctype.Fields)
                    //            {
                    //                if (fld.Group == "_Hidden")
                    //                    continue;
                    //                SPField field = null;
                    //                try
                    //                {
                    //                    field = list.Fields.GetFieldByInternalName(fld.InternalName);
                    //                }
                    //                catch (ArgumentException)
                    //                {
                    //                }

                    //                if (field != null && !field.ReadOnlyField)
                    //                {
                    //                    field.Required = fld.Required;
                    //                    if (!field.CanToggleHidden)
                    //                    {
                    //                        Type type = field.GetType();
                    //                        MethodInfo mi = type.GetMethod("SetFieldBoolValue", BindingFlags.NonPublic | BindingFlags.Instance);
                    //                        mi.Invoke(field, new object[] { "CanToggleHidden", true });
                    //                    }
                    //                    field.Hidden = fld.Hidden;
                    //                    field.Update();
                    //                }
                    //            }
                    //        }
                    //    }
                    //}
        }
    }
}
